package com.xl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.sql.*;

// 这是一个使用JDBC连接MySQL数据库进行员工信息增删改查操作的示例类
public class JdbcExample {

    // 定义一个静态的Logger对象，用于记录日志
    private static final Logger logger = LoggerFactory.getLogger(JdbcExample.class);
    // 数据库连接URL，指定了数据库的地址、数据库名、是否使用SSL以及服务器时区等信息
    private static final String URL = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
    private static final String USER = "root";
    private static final String PASSWORD = "123456";


    // 静态代码块，用于加载数据库驱动
    static {
        try {
            // 通过Class.forName方法加载MySQL JDBC驱动类
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            // 如果加载驱动失败，记录错误日志
            logger.error("无法加载数据库驱动", e);
        }
    }

    // 获取数据库连接的方法
    public static Connection getConnection() {
        Connection connection = null;
        try {
            // 使用DriverManager.getConnection方法获取数据库连接
            connection = DriverManager.getConnection(URL, USER, PASSWORD);
            // 成功建立连接后，记录日志
            logger.info("成功建立数据库连接");
        } catch (SQLException e) {
            // 更详细的异常处理示例
            if (e.getSQLState().equals("08001")) {
                logger.error("无法连接到数据库服务器", e);
            } else if (e.getSQLState().equals("28000")) {
                logger.error("数据库身份验证失败", e);
            } else {
                logger.error("获取数据库连接失败", e);
            }
        }
        return connection;
    }

    // 插入员工数据的方法
    public static void insertEmployeeWithTypeMapping(String name, int age, double salary) {
        // 定义插入数据的SQL语句，使用预编译语句防止SQL注入
        String sql = "INSERT INTO employees (name, age, salary) VALUES (?,?,?)";
        try (
                // 获取数据库连接
                Connection connection = getConnection();
                // 创建预编译语句对象
                PreparedStatement preparedStatement = connection.prepareStatement(sql)
        ) {
            // 设置预编译语句的参数
            preparedStatement.setString(1, name);
            preparedStatement.setInt(2, age);

            // 将Java的double类型映射到数据库的DECIMAL类型
            // Java数据类型与数据库数据类型的映射
            preparedStatement.setObject(3, salary, Types.DECIMAL);
            // 执行插入操作，返回受影响的行数
            int rowsInserted = preparedStatement.executeUpdate();
            if (rowsInserted > 0) {
                // 如果插入成功，记录日志
                logger.info("成功插入一条员工记录");
            }
        } catch (SQLException e) {
            // 如果插入失败，记录错误日志
            logger.error("插入员工记录失败", e);
        }
    }

    // 查询所有员工数据的方法
    public static void queryAllEmployees() {
        // 定义查询数据的SQL语句
        String sql = "SELECT * FROM employees";
        try (
                // 获取数据库连接
                Connection connection = getConnection();
                // 创建Statement对象
                Statement statement = connection.createStatement();
                // 执行查询操作，返回结果集
                ResultSet resultSet = statement.executeQuery(sql)
        ) {
            // 遍历结果集
            while (resultSet.next()) {
                // 获取员工的ID、姓名、年龄和工资
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                double salary = resultSet.getDouble("salary");
                // 记录查询到的员工信息
                logger.info("员工ID: {}, 姓名: {}, 年龄: {}, 工资: {}", id, name, age, salary);
            }
        } catch (SQLException e) {
            // 如果查询失败，记录错误日志
            logger.error("查询员工记录失败", e);
        }
    }

    // 更新员工数据的方法
    public static void updateEmployee(int id, int newAge) {
        // 定义更新数据的SQL语句，使用预编译语句防止SQL注入
        String sql = "UPDATE employees SET age =? WHERE id =?";
        try (
                // 获取数据库连接
                Connection connection = getConnection();
                // 创建预编译语句对象
                PreparedStatement preparedStatement = connection.prepareStatement(sql)
        ) {
            // 设置预编译语句的参数
            preparedStatement.setInt(1, newAge);
            preparedStatement.setInt(2, id);
            // 执行更新操作，返回受影响的行数
            int rowsUpdated = preparedStatement.executeUpdate();
            if (rowsUpdated > 0) {
                // 如果更新成功，记录日志
                logger.info("成功更新员工记录");
            }
        } catch (SQLException e) {
            // 如果更新失败，记录错误日志
            logger.error("更新员工记录失败", e);
        }
    }

    // 删除员工数据的方法
    public static void deleteEmployee(int id) {
        // 定义删除数据的SQL语句，使用预编译语句防止SQL注入
        String sql = "DELETE FROM employees WHERE id =?";
        try (
                // 获取数据库连接
                Connection connection = getConnection();
                // 创建预编译语句对象
                PreparedStatement preparedStatement = connection.prepareStatement(sql)
        ) {
            // 设置预编译语句的参数
            preparedStatement.setInt(1, id);
            // 执行删除操作，返回受影响的行数
            int rowsDeleted = preparedStatement.executeUpdate();
            if (rowsDeleted > 0) {
                // 如果删除成功，记录日志
                logger.info("成功删除员工记录");
            }
        } catch (SQLException e) {
            // 如果删除失败，记录错误日志
            logger.error("删除员工记录失败", e);
        }
    }

    // 批量操作示例：批量插入数据
    public static void batchInsertEmployees(List<String> names, List<Integer> ages, List<Double> salaries) {
        // SQL 插入语句，插入员工的名字、年龄和薪资
        String sql = "INSERT INTO employees (name, age, salary) VALUES (?,?,?)";
        try (Connection connection = getConnection();  // 获取数据库连接
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {  // 创建预处理语句
            // 遍历员工数据列表，批量添加到 PreparedStatement 中
            for (int i = 0; i < names.size(); i++) {
                preparedStatement.setString(1, names.get(i));  // 设置第1个参数（name）
                preparedStatement.setInt(2, ages.get(i));     // 设置第2个参数（age）
                preparedStatement.setDouble(3, salaries.get(i)); // 设置第3个参数（salary）
                preparedStatement.addBatch();  // 将当前的插入操作加入批处理
            }
            // 执行批量插入操作,int[]表示每个批次操作更新的行数
            int[] updateCounts = preparedStatement.executeBatch();
            // 统计成功插入的记录数
            int totalInserted = 0;
            for (int count : updateCounts) {
                totalInserted += count;  // 累加每次批量插入的影响行数
            }
            logger.info("成功批量插入 {} 条员工记录", totalInserted);  // 输出成功插入的记录数
        } catch (SQLException e) {
            logger.error("批量插入员工记录失败", e);  // 捕获异常并记录错误日志
        }
    }

    public static void callStoredProcedureToGetEmployeeCount() {
        String sql = "{call get_employee_count()}";  // 这里是存储过程的调用语句,需要先有存储过程,可以传参
        try (Connection connection = getConnection();
             // 获取数据库连接,使用了 Java 7 引入的 try-with-resources 语法，表示在 try 块结束时会自动关闭Connection、Statement和ResultSet
             CallableStatement callableStatement = connection.prepareCall(sql)) {  // 使用连接创建 CallableStatement 对象,类似于 PreparedStatement，但专门用于调用存储过程
            ResultSet resultSet = callableStatement.executeQuery();  // 执行查询，返回结果集,被resultSet保存
            if (resultSet.next()) {  // 如果结果集有数据
                int count = resultSet.getInt(1);  // 获取查询结果中的第一个字段（员工总数）注意，1 表示第一个字段，字段的索引从 1 开始
                logger.info("员工总数: {}", count);  // 打印员工总数
            }
        } catch (SQLException e) {
            logger.error("调用存储过程获取员工总数失败", e);  // 如果有异常，记录错误
        }
    }

    // 假设数据库中有如下存储过程定义（MySQL示例）
    // DELIMITER //
    // CREATE PROCEDURE get_employee_count()
    // BEGIN
    //     SELECT COUNT(*) FROM employees;
    // END //
    // DELIMITER ;



    // 主方法，程序的入口点
    public static void main(String[] args) {
        // 数据类型映射插入示例
        insertEmployeeWithTypeMapping("Bob", 30, 9000.00);
        // 批量插入示例
        List<String> names = new ArrayList<>();
        List<Integer> ages = new ArrayList<>();
        List<Double> salaries = new ArrayList<>();

        names.add("Charlie");
        ages.add(32);
        salaries.add(9500.00);
        names.add("David");
        ages.add(33);
        salaries.add(10000.00);
        batchInsertEmployees(names, ages, salaries);

        // 查询所有数据
        queryAllEmployees();
        // 更新数据
        updateEmployee(1, 31);
        // 再次查询所有数据
        queryAllEmployees();
        // 删除数据
        deleteEmployee(1);
        // 最后查询所有数据
        queryAllEmployees();
        // 调用存储过程示例
        callStoredProcedureToGetEmployeeCount();
    }
}